`timescale 1 ns/ 1 ns
//抽时间画一个RTL逻辑视图  模块综合图
module UDP_Send(
	Clk			,
	Rst_n			,
//	Go				,
	Tx_Done		,	
	des_mac		,
	src_mac		,
	des_port		,
	src_port		,
	des_ip		,
	src_ip		,
	data_length	,
	GMII_GTXC	,
   rgmii_tx_clk,
   rgmii_txd	,
   rgmii_txen	,
	wrreq			,
	wrdata		,
	wrclk			,
	aclr			,
	wrusedw		
);

	input Clk;
	input Rst_n;

//	input Go;
	output reg Tx_Done;
	input [47:0]des_mac;  //MAC层(链路层)
	input [47:0]src_mac;
	input [15:0]des_port;//传输
	input [15:0]src_port;
	input [31:0]des_ip;//网络
	input [31:0]src_ip;
	input [15:0]data_length;  //数据长度
	input GMII_GTXC;  //GMII接口时钟
	output rgmii_tx_clk; //RGMII时钟
	output [3:0]rgmii_txd;//rgmii千兆以太网数据输出端口
	output rgmii_txen;//发送使能信号
	input wrreq;//写请求
	input [7:0]wrdata;//写数据(缓存到FIFO的)
	input wrclk; //fifo写数据时钟
	input aclr;//异步复位信号
	output [11:0]wrusedw;	//写入FIFO数据个数
	reg[7:0]GMII_TXD;//GMII千兆以太网数据接口
	reg GMII_TXEN;//GMII接口发送使能
	reg [7:0]GMII_TXD_reg;//将输出寄存 -> 减少传播路径 减少延迟
	reg GMII_TXEN_reg;	//GMII发送使能寄存
	reg [47:0]des_mac_reg;//对输入到UDP_SEND模块的信号进行寄存
	reg [47:0]src_mac_reg;
	reg [15:0]des_port_reg;
	reg [15:0]src_port_reg;
	reg [31:0]des_ip_reg;
	reg [31:0]src_ip_reg;
	reg [15:0]data_length_reg;
	reg Go;  //发送使能
	localparam FRAME_TYPE = 16'h0800;

	//将所有输入信号存储进入寄存器  目的是减少传输延迟
always@(posedge GMII_GTXC)
	if(Go)begin
		des_mac_reg <= #1 des_mac;
		src_mac_reg <= #1 src_mac;
		des_port_reg <= #1 des_port;
		src_port_reg <= #1 src_port;
		des_ip_reg <= #1 des_ip;
		src_ip_reg <= #1 src_ip;
		data_length_reg <= #1 data_length;
	end
		

	//对输出端口加一级寄存器，以方便使用IO输出寄存器(GMII_TXD是输出端口，在输出前最好加一级寄存器进行寄存)
always@(posedge GMII_GTXC)begin
		GMII_TXD <= #1 GMII_TXD_reg;
		GMII_TXEN <= #1 GMII_TXEN_reg;
	end

	
	reg [4:0]cnt_header;	//前导码计数器
	reg [4:0]cnt_des_mac;	//目标MAC地址计数器
	reg [4:0]cnt_src_mac;	//源MAC地址计数器
	reg [1:0]cnt_frame_type;	//帧类型计数器	
	reg [4:0]cnt_ip_header;	//IP报头计数器
	reg [4:0]cnt_udp_header;	//UDP报头计数器
	reg [11:0]cnt_data;	//数据计数器
	reg [4:0]cnt_crc;	//crc计数器
	reg [31:0]ip_header[4:0];//IP头部->20B
	reg [31:0]udp_header[1:0];//UDP头部->8B
	wire [31:0]crc_result;//crc 4B
	reg [9:0]state;//状态机
	wire [15:0]ip_checksum;//IP校验和
	wire [7:0]fifo_rddata;//读出的数据
	reg fifo_rdreq;//读请求
	wire [11:0]rdusedw;//读出数据的个数
//GMII接口转RGMII接口  8位线宽->4位线宽   读写频率不变，采用双沿采样保证带宽一样 (1Gbps)	
gmii_to_rgmii	gmii_to_rgmii_inst(
		.gmii_gtxc(GMII_GTXC),
		.gmii_txd(GMII_TXD),
		.gmii_txen(GMII_TXEN),
		.rgmii_gtxc(rgmii_tx_clk),
		.rgmii_txd(rgmii_txd),
		.rgmii_txen(rgmii_txen)
	);
//将数据缓存至FIFO，开始发送时将数据读出（先缓存）
eth_dcfifo eth_dcfifo_inst(
		.aclr(aclr),
		.data(wrdata),  //对输入的数据进行缓存
		.rdclk(GMII_GTXC),
		.rdreq(fifo_rdreq),
		.wrclk(wrclk), 
		.wrreq(wrreq), //写请求
		.q(fifo_rddata),
		.rdusedw(rdusedw),
		.wrusedw(wrusedw)
	);
	
	reg [1:0]ctrl_state;
	reg [7:0]delay_cnt;
//当FIFO中可读数据大于数据长度时进行跳转，每发完一帧在进行跳转最后回到IDLE状态
//Go信号是启动发送使能信号；当FIFO的可读数据大于应用层数据个数即拉高。
//每次发送应用层数据只需发送指定个数即可（通过计数器限制发送FIFO缓存中的数据个数，确保每次发送的数据是完整的应用层数据），这样就保证了不会串数据
always@(posedge GMII_GTXC or negedge Rst_n)
	if(!Rst_n)
		begin
			ctrl_state <= #1 0;
			Go <= #1 1'b0;
			delay_cnt <= #1 0;
		end
	else 
		begin
			case(ctrl_state)
				0:
					if(rdusedw >= data_length)begin  //rdusedw表示FIFO中可读数据个数，wrusedw表示已写入FIFO中的数据个数
						ctrl_state <= #1 2'd1;       //当FIFO中可读数据个数大于data_length时，拉高Go信号 、状态跳转
						Go <= #1 1'b1;
					end
					else begin
						ctrl_state <= 0;
						Go <= #1 1'b0;
					end
					
				1:
					begin
						Go <= #1 1'b0;
						if(Tx_Done) //一帧数据发送完毕Tx_Done拉高   
							ctrl_state <= #1 2'd2;
						else
							ctrl_state <= #1 2'd1;
					end
					
				2:      //每次发完延迟一段时间
					if(delay_cnt == 8'd255)begin
						ctrl_state <= #1 2'd3;
						delay_cnt <= #1 0;
					end
					else begin
						ctrl_state <= #1 2'd2;
						delay_cnt <= #1 delay_cnt + 1'b1;
					end
				
				3: ctrl_state <= #1 2'd0;
			endcase
		end
//按照IP头部格式进行存储及发送
always@(posedge GMII_GTXC)
	begin
		ip_header[0][31:24] <= #1 8'h45;	//协议版本+首部长度
		ip_header[0][23:16] <= #1 8'h00;	//服务类型
		ip_header[0][15:0]  <= #1 data_length_reg + 8'd28; //IP数据报总长度（IP报头+数据）data_length_reg->应用层数据长度
		ip_header[1][31:0] <= #1 32'd0;	//数据包标识 + 标识+分段偏移
		ip_header[2][31:24] <= #1 8'd64;	//生存时间 64
		ip_header[2][23:16] <= #1 8'd17;	//UDP协议
		ip_header[2][15:0] <= #1 ip_checksum;	//IP 校验和
		ip_header[3][31:0] <= #1 src_ip_reg;	//源IP地址	
		ip_header[4][31:0] <= #1 des_ip_reg;	//目的IP地址
	end
		
//传输层报头	
always@(posedge GMII_GTXC)
	begin
		udp_header[0][31:16] <= #1 src_port_reg;	//源端口号
		udp_header[0][15:0]  <= #1 des_port_reg;	//目的端口号
		udp_header[1][31:16] <= #1 data_length_reg + 8'd8;	//UDP数据报总长度（UDP报头+数据）
		udp_header[1][15:0] <= #1 16'h00;		//UDP报头校验和  忽略
	end
		
//IP校验和;这里更适合采用流水线操作，牺牲时间换时序	
	wire [31:0]suma,sumb;
assign suma = ip_header[0][31:16] + ip_header[0][15:0]
			  + ip_header[1][31:16] + ip_header[1][15:0]
			  + ip_header[2][31:16] + ip_header[3][31:16]
			  + ip_header[3][15:0]  + ip_header[4][31:16] + ip_header[4][15:0];
			  
assign sumb = (suma[31:16] + suma[15:0]);
assign ip_checksum = sumb[31:16]? ~(sumb[31:16] + sumb[15:0]):~sumb[15:0];
//采用独热码进行状态定义	
	localparam
		IDLE = 				10'b0000000001,
		SEND_HEADER = 		10'b0000000010,
		SEND_DES_MAC = 	10'b0000000100,
		SEND_SRC_MAC = 	10'b0000001000,
		SEND_FRAME_TYPE = 10'b0000010000,
		SEND_IP_HEADER = 	10'b0000100000,
		SEND_UDP_HEADER = 10'b0001000000,
		SEND_DATA = 		10'b0010000000,
		SEND_CRC = 			10'b0100000000;
	
	wire CRC_Reset;
	reg CRC_EN;
	
assign CRC_Reset = (state == IDLE);

//计算CRC校验码	
CRC32_D8 CRC32_D8(
		.Clk(GMII_GTXC),
		.Reset(CRC_Reset),
		.Data_in(GMII_TXD_reg), 
		.Enable(CRC_EN),
		.Crc(),
		.CrcNext(),
		.Crc_eth(crc_result)
	);
//发送每部分都采用一个单独计数器
//先发送各个层级的报头（通过端口输入）再发送FIFO中缓存的应用层数据 最后发送CRC校验；
always@(posedge GMII_GTXC or negedge Rst_n)
	if(!Rst_n)begin
		state <= #1 IDLE;
		cnt_header <= #1 0;	//前导码计数器
		cnt_des_mac <= #1 0;	//目标MAC地址计数器
		cnt_src_mac <= #1 0;	//源MAC地址计数器
		cnt_frame_type <= #1 0;	//帧类型计数器
		cnt_ip_header <= #1 0;	//IP报头计数器
		cnt_udp_header <= #1 0;	//UDP报头计数器
		cnt_data <= #1 0;	//数据计数器 
		cnt_crc <= #1 0;	//crc计数器
		GMII_TXD_reg <= #1 0;
		GMII_TXEN_reg <= #1 0;
		fifo_rdreq <= #1 1'b0;
		Tx_Done <= #1 1'b0;
		CRC_EN <= #1 1'b0;
	end	
	else begin
		case(state)
			IDLE:
				begin
					GMII_TXEN_reg <= #1 0;
					Tx_Done <= #1 1'b0; 
					if(Go)  //当FIFO中可读数据个数大于data_length时，拉高Go信号  Go信号是发送启动使能信号
						state <= #1 SEND_HEADER;	// 发送前导码
					else
						state <= #1 IDLE;
				end					
			SEND_HEADER:
				begin
					GMII_TXEN_reg <= #1 1;
					if(cnt_header >= 7)begin
						cnt_header <= #1 0;
						state <= #1 SEND_DES_MAC;
					end	
					else begin
						cnt_header <= #1 cnt_header + 1'b1;
						state <= #1 SEND_HEADER;
					end	
					case(cnt_header)  //先发送Mac帧报头的前导码
						0,1,2,3,4,5,6:GMII_TXD_reg <= #1 8'h55;
						7:GMII_TXD_reg <= #1 8'hd5;
						default:GMII_TXD_reg <= #1 8'h55;
					endcase
				end
					
				
			SEND_DES_MAC:
				begin
					CRC_EN <= #1 1'b1;
					if(cnt_des_mac >= 5)begin  //6个字节（计数器0~5）
						cnt_des_mac <= #1 0;
						state <= #1 SEND_SRC_MAC;  //发送完毕DES_MAC转入发送SRC_MAC
					end	
					else begin
						cnt_des_mac <= #1 cnt_des_mac + 1'b1;
						state <= #1 SEND_DES_MAC;
					end	
					case(cnt_des_mac)
						0:GMII_TXD_reg <= #1 des_mac_reg[47:40];
						1:GMII_TXD_reg <= #1 des_mac_reg[39:32];
						2:GMII_TXD_reg <= #1 des_mac_reg[31:24];
						3:GMII_TXD_reg <= #1 des_mac_reg[23:16];
						4:GMII_TXD_reg <= #1 des_mac_reg[15:8];
						5:GMII_TXD_reg <= #1 des_mac_reg[7:0];
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end
					
				
			SEND_SRC_MAC:
				begin
					if(cnt_src_mac >= 5)begin
						cnt_src_mac <= #1 0;
						state <= #1 SEND_FRAME_TYPE;
					end	
					else begin
						cnt_src_mac <= #1 cnt_src_mac + 1'b1;
						state <= #1 SEND_SRC_MAC;
					end	
					case(cnt_src_mac)
						0:GMII_TXD_reg <= #1 src_mac_reg[47:40];
						1:GMII_TXD_reg <= #1 src_mac_reg[39:32];
						2:GMII_TXD_reg <= #1 src_mac_reg[31:24];
						3:GMII_TXD_reg <= #1 src_mac_reg[23:16];
						4:GMII_TXD_reg <= #1 src_mac_reg[15:8];
						5:GMII_TXD_reg <= #1 src_mac_reg[7:0];
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end
			
			SEND_FRAME_TYPE:
				begin
					if(cnt_frame_type >= 1)begin  //帧类型2B，使用GMII接口每次发1B，因此只需计数器记到0~1这个范围即可
						cnt_frame_type <= #1 0;
						state <= #1 SEND_IP_HEADER;
					end	
					else begin
						cnt_frame_type <= #1 cnt_frame_type + 1'b1;
						state <= #1 SEND_FRAME_TYPE;
					end	
					case(cnt_frame_type)   //发送帧类型
						0:GMII_TXD_reg <= #1 FRAME_TYPE[15:8];
						1:GMII_TXD_reg <= #1 FRAME_TYPE[7:0];
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end	
				
			SEND_IP_HEADER: //发送网络层->IP报头
				begin
					if(cnt_ip_header >= 19)begin  //IP头部20B,计数器范围0~19即可
						cnt_ip_header <= #1 0;
						state <= #1 SEND_UDP_HEADER;
					end	
					else begin
						cnt_ip_header <= #1 cnt_ip_header + 1'b1;
						state <= #1 SEND_IP_HEADER;
					end	
					case(cnt_ip_header)  //根据协议决定发送顺序
						0 :GMII_TXD_reg <= #1 ip_header[0][31:24];
						1 :GMII_TXD_reg <= #1 ip_header[0][23:16];
						2 :GMII_TXD_reg <= #1 ip_header[0][15:8];
						3 :GMII_TXD_reg <= #1 ip_header[0][7:0];
						4 :GMII_TXD_reg <= #1 ip_header[1][31:24];
						5 :GMII_TXD_reg <= #1 ip_header[1][23:16];
						6 :GMII_TXD_reg <= #1 ip_header[1][15:8];
						7 :GMII_TXD_reg <= #1 ip_header[1][7:0];
						8 :GMII_TXD_reg <= #1 ip_header[2][31:24];
						9 :GMII_TXD_reg <= #1 ip_header[2][23:16];
						10:GMII_TXD_reg <= #1 ip_header[2][15:8];
						11:GMII_TXD_reg <= #1 ip_header[2][7:0];
						12:GMII_TXD_reg <= #1 ip_header[3][31:24];
						13:GMII_TXD_reg <= #1 ip_header[3][23:16];
						14:GMII_TXD_reg <= #1 ip_header[3][15:8];
						15:GMII_TXD_reg <= #1 ip_header[3][7:0];
						16:GMII_TXD_reg <= #1 ip_header[4][31:24];
						17:GMII_TXD_reg <= #1 ip_header[4][23:16];
						18:GMII_TXD_reg <= #1 ip_header[4][15:8];
						19:GMII_TXD_reg <= #1 ip_header[4][7:0];						
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end
			
			SEND_UDP_HEADER: //发送传输层报头 8B
				begin
					if(cnt_udp_header >= 7)begin  //计数器范围0~7即可
						cnt_udp_header <= #1 0;
						fifo_rdreq <= #1 1'b1;  //开始读出应用层数据； 链路->网络->传输->应用
						state <= #1 SEND_DATA;  //跳转至发送应用层数据（有效数据、用户数据）
					end
					else begin
						cnt_udp_header <= #1 cnt_udp_header + 1'b1;
						state <= #1 SEND_UDP_HEADER;
					end
					case(cnt_udp_header)
						0 :GMII_TXD_reg <= #1 udp_header[0][31:24];
						1 :GMII_TXD_reg <= #1 udp_header[0][23:16];
						2 :GMII_TXD_reg <= #1 udp_header[0][15:8];
						3 :GMII_TXD_reg <= #1 udp_header[0][7:0];
						4 :GMII_TXD_reg <= #1 udp_header[1][31:24];
						5 :GMII_TXD_reg <= #1 udp_header[1][23:16];
						6 :GMII_TXD_reg <= #1 udp_header[1][15:8];
						7 :GMII_TXD_reg <= #1 udp_header[1][7:0];			
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end
			
			SEND_DATA:  //发送应用层
				begin
					if(cnt_data >= data_length_reg - 1)begin//通过计数器保证发送完整的一帧数据，记数范围0~data_length_reg -1,确保一帧所有数据都会被发送
						cnt_data <= #1 0;
						state <= #1 SEND_CRC;  //跳转到CRC校验部分，已经发送完了应用层数据
						fifo_rdreq <= #1 1'b0;
						GMII_TXD_reg <= #1 fifo_rddata;  //发送应用层数据的最后一个数据
					end
					else begin
						cnt_data <= #1 cnt_data + 1'b1;
						state <= #1 SEND_DATA;
						GMII_TXD_reg <= #1 fifo_rddata;
					end
				end
				
			SEND_CRC:
				begin
					CRC_EN <= #1 1'b0;
					if(cnt_crc >= 3)begin  //CRC部分4B 计数器范围只需0~3即可
						cnt_crc <= #1 0;
						state <= #1 IDLE; //发送完后跳转到IDLE；等待
						Tx_Done <= #1 1'b1;  //完整的一帧数据：链路层+网络层+传输层+应用层+校验 发送完后Tx_Done拉高
					end
					else begin
						cnt_crc <= #1 cnt_crc + 1'b1;
						state <= #1 SEND_CRC;
					end
					case(cnt_crc) //不属于if-else部分 不受条件控制一定会被执行
						0 :GMII_TXD_reg <= #1 crc_result[31:24];
						1 :GMII_TXD_reg <= #1 crc_result[23:16];
						2 :GMII_TXD_reg <= #1 crc_result[15:8];
						3 :GMII_TXD_reg <= #1 crc_result[7:0];				
						default:GMII_TXD_reg <= #1 8'hff;
					endcase
				end
			default:begin state <= #1 IDLE;end
		endcase
	end		
	
endmodule
